3f9e7d60PWZJeVh5xdnk0nLUdxlqEA docs/eps/xenlogo.eps
3f9e7d63lTwQbp2fnx7yY93epWS-eQ docs/figs/dummy
3f9e7d564bWFB-Czjv1qdmE6o0GqNg docs/interface.tex
+4022a73cgxX1ryj1HgS-IwwB6NUi2A docs/pdb.txt
3f9e7d58t7N6hjjBMxSn-NMxBphchA docs/style.tex
3f9e7d5bz8BwYkNuwyiPVu7JJG441A docs/xenstyle.cls
3f815144d1vI2777JI-dO4wk49Iw7g extras/mini-os/Makefile
3f13d81e6Z6806ihYYUw8GVKNkYnuw tools/misc/xen_nat_enable.README
3f1668d4F29Jsw0aC0bJEIkOBiagiQ tools/misc/xen_read_console.c
3f87ba90EUVPQLVOlFG0sW89BCwouQ tools/misc/xen_refresh_dev.c
+4022a73cEKvrYe_DVZW2JlAxobg9wg tools/nsplitd/Makefile
+4022a73cKms4Oq030x2JBzUB426lAQ tools/nsplitd/nsplitd.c
3fbca441SjQr8vJwTQIgH1laysaWog tools/xc/Makefile
3fbba6dbDfYvJSsw9500b4SZyUhxjQ tools/xc/lib/Makefile
3fbba6dc1uU7U3IFeF6A-XEOYF2MkQ tools/xc/lib/rpm.spec
3ddb79bdIKgipvGoqExEQ7jawfVowA xen/arch/i386/pci-i386.h
3ddb79bdHe6_Uij4-glW91vInNtBYQ xen/arch/i386/pci-irq.c
3ddb79bcZ_2FxINljqNSkqa17ISyJw xen/arch/i386/pci-pc.c
+4022a73czgX7d-2zfF_cb33oVemApQ xen/arch/i386/pdb-stub.c
3ddb79bc1_2bAt67x9MFCP4AZrQnvQ xen/arch/i386/process.c
3ddb79bc7KxGCEJsgBnkDX7XjD_ZEQ xen/arch/i386/rwlock.c
3ddb79bcrD6Z_rUvSDgrvjyb4846Eg xen/arch/i386/setup.c
3ddb79bddEYJbcURvqqcx99Yl2iAhQ xen/common/block.c
3ddb79bdrqnW93GR9gZk1OJe1qK-iQ xen/common/brlock.c
3fb10d07GscSWPKxBqpvNfU-dYfa0g xen/common/console.c
+4022a73c_BbDFd2YJ_NQYVvKX5Oz7w xen/common/debug-linux.c
+4022a73c_KPZ1VEbYOrpAhQffd01kA xen/common/debug-linux.h
3fa152581E5KhrAtqZef2Sr5NKTz4w xen/common/debug.c
3ddb79bdLX_P6iB7ILiblRLWvebapg xen/common/dom0_ops.c
3e6377e4i0c9GtKN65e99OtRbw3AZw xen/common/dom_mem_ops.c
3ddb79c3xjYnrv5t3VqYlR4tNEOl4Q xen/include/asm-i386/page.h
3e450943kzme29HPCtq5HNOVQkddfw xen/include/asm-i386/param.h
3ddb79c3ysKUbxZuwKBRK3WXU2TlEg xen/include/asm-i386/pci.h
+4022a73diKn2Ax4-R4gzk59lm1YdDg xen/include/asm-i386/pdb.h
3ddb79c3nm2zdzeO6Mj8g7ex3txgGw xen/include/asm-i386/pgalloc.h
3ddb79c2QF5-pZGzuX4QukPCDAl59A xen/include/asm-i386/processor.h
3ddb79c3mbqEM7QQr3zVq7NiBNhouA xen/include/asm-i386/ptrace.h
ifname=dummy Don't use any network interface.
- ser_baud=xxx Enable serial I/O and set the baud rate.
+ ser_baud=xxx Enable serial I/O and set the baud rate (COM1)
dom0_mem=xxx Set the initial amount of memory for domain0.
-
+
+ pdb=xxx Enable the pervasive debugger. See docs/pdb.txt
+ xxx defines how the gdb stub will communicate:
+ com1 use com1
+ com1H use com1 (with high bit set)
+ com2 use on com2
+ com2H use com2 (with high bit set)
It's probably a good idea to join the Xen developer's mailing list on
Sourceforge: http://lists.sourceforge.net/lists/listinfo/xen-devel
--- /dev/null
+Pervasive Debugging
+===================
+
+040205 Alex Ho (alex.ho@cl.cam.ac.uk)
+
+Introduction
+------------
+
+The pervasive debugging project is leveraging Xen to
+debug distributed systems. We have added a gdb stub
+to Xen to allow for remote debugging of both Xen and
+guest operating systems. More information about the
+pervasive debugger is available at: http://www.cl.cam.ac.uk/netos/pdb
+
+
+Implementation
+--------------
+
+The gdb stub communicates with gdb running over a serial line.
+The main entry point is pdb_handle_exception() which is invoked
+from: pdb_key_pressed() ('D' on the console)
+ do_int3_exception() (interrupt 3: breakpoint exception)
+ do_debug() (interrupt 1: debug exception)
+
+This accepts characters from the serial port and passes gdb
+commands to pdb_process_command() which implements the gdb stub
+interface. This file draws heavily from the kgdb project and
+sample gdbstub provided with gdb.
+
+The stub can examine registers, single step and continue, and
+read and write memory (in Xen, a domain, or a Linux process'
+address space). The debugger does not currently trace the
+current process, so all bets are off if context switch occurs
+in the domain.
+
+
+Setup
+-----
+
+ +-------+ telnet +-----------+ serial +-------+
+ | GDB |--------| nsplitd |--------| Xen |
+ +-------+ +-----------+ +-------+
+
+To run pdb, Xen must be appropriately configured and
+a suitable serial interface attached to the target machine.
+GDB and nsplitd can run on the same machine.
+
+Xen Configuration
+
+ Add the "pdb=xxx" option to your Xen boot command line
+ where xxx is one of the following values:
+ com1 gdb stub should communicate on com1
+ com1H gdb stub should communicate on com1 (with high bit set)
+ com2 gdb stub should communicate on com2
+ com2H gdb stub should communicate on com2 (with high bit set)
+
+ Symbolic debugging infomration is quite helpful too:
+ xeno.bk/xen/arch/i386/Rules.mk
+ add -g to CFLAGS to compile Xen with symbols
+ xeno.bk/xenolinux-2.4.24-sparse/arch/xeno/Makefile
+ add -g to CFLAGS to compile XenoLinux with symbols
+
+ You may also want to consider dedicating a register to the
+ frame pointer (disable the -fomit-frame-pointer compile flag).
+
+ When booting Xen and domain 0, look for the console text
+ "Initializing pervasive debugger (PDB)" just before DOM0 starts up.
+
+Serial Port Configuration
+
+ pdb expects to communicate with gdb using the serial port. Since
+ this port is often shared with the machine's console output, pdb can
+ discriminate its communication by setting the high bit of each char.
+
+ A new tool has been added to the source tree which splits
+ the serial output from a remote machine into two streams:
+ one stream (without the high bit) is the console and
+ one stream (with the high bit stripped) is the pdb communication.
+
+ See: xeno.bk/tools/nsplitd
+
+ Note: nsplitd was originally written for the Nemesis project
+ at Cambridge.
+
+ Usage:
+ %telnet <hostname> <port>
+ This is the console of the remote machine. You will probably
+ want to set telnet in char mode (or create a .telnetrc file).
+ <hostname> and <port> are for your instance of nsplitd.
+ You should be able to press 'h' to display a list of keyboard
+ handlers. In particular, you should see:
+ key 'D' (ascii '44') => enter pervasive debugger
+
+ After nsplitd accepts a connection on <port>, it starts listening
+ on port <port + 1>. Characters sent to the <port + 1> will have the
+ high bit set and vice versa for characters received.
+
+ Note: if you are not using a serial console and can dedicate your
+ serial line to pdb messages, then edit pdb_put_char and pdb_get_char
+ in pdb-stub.c to remove the high bit tests.
+
+GDB 6.0
+ pdb has been tested with gdb 6.0. It should also work with
+ earlier versions.
+
+
+Usage
+-----
+
+1. Boot Xen and XenoLinux
+2. Interrupt Xen by pressing 'D' at the console
+ You should see the console message:
+ pdb_handle_exception [0x88][0xfc5c9d88]
+ At this point Xen is waiting for gdb commands on the serial line.
+3. Attach with gdb
+ (gdb) file xeno.bk/xen/xen
+ Reading symbols from xeno.bk/xen/xen...done.
+ (gdb) target remote <hostname>:<port + 1> /* contact nsplitd */
+ Remote debugging using serial.srg:12131
+ continue_cpu_idle_loop () at current.h:10
+ warning: shared library handler failed to enable breakpoint
+ (gdb) break __enter_scheduler
+ Breakpoint 1 at 0xfc510a94: file schedule.c, line 330.
+ (gdb) cont
+ Continuing.
+
+ Program received signal SIGTRAP, Trace/breakpoint trap.
+ __enter_scheduler () at schedule.c:330
+ (gdb) step
+ (gdb) step
+ (gdb) print next /* the variable prev has been optimized away! */
+ $1 = (struct task_struct *) 0x0
+ (gdb) delete
+ Delete all breakpoints? (y or n) y
+4. You can add additional symbols to gdb
+ (gdb) add-sym xenolinux-2.4.24/vmlinux
+ add symbol table from file "xenolinux-2.4.24/vmlinux" at
+ (y or n) y
+ Reading symbols from xenolinux-2.4.24/vmlinux...done.
+ (gdb) x/s cpu_vendor_names[0]
+ 0xc01530d2 <cpdext+62898>: "Intel"
+ (gdb) break free_uid
+ Breakpoint 2 at 0xc0012250
+ (gdb) cont
+ Continuing. /* run a command in domain 0 */
+
+ Program received signal SIGTRAP, Trace/breakpoint trap.
+ free_uid (up=0xbffff738) at user.c:77
+
+ (gdb) print *up
+ $2 = {__count = {counter = 0}, processes = {counter = 135190120}, files = {
+ counter = 0}, next = 0x395, pprev = 0xbffff878, uid = 134701041}
+ (gdb) finish
+ Run till exit from #0 free_uid (up=0xbffff738) at user.c:77
+
+ Program received signal SIGTRAP, Trace/breakpoint trap.
+ release_task (p=0xc2da0000) at exit.c:51
+ (gdb) print *p
+$3 = {state = 4, flags = 4, sigpending = 0, addr_limit = {seg = 3221225472},
+ exec_domain = 0xc016a040, need_resched = 0, ptrace = 0, lock_depth = -1,
+ counter = 1, nice = 0, policy = 0, mm = 0x0, processor = 0,
+ cpus_runnable = 1, cpus_allowed = 4294967295, run_list = {next = 0x0,
+ prev = 0x0}, sleep_time = 18995, next_task = 0xc017c000,
+ prev_task = 0xc2f94000, active_mm = 0x0, local_pages = {next = 0xc2da0054,
+ prev = 0xc2da0054}, allocation_order = 0, nr_local_pages = 0,
+ binfmt = 0xc016c6a0, exit_code = 0, exit_signal = 17, pdeath_signal = 0,
+ personality = 0, did_exec = -1, task_dumpable = 1, pid = 917, pgrp = 914,
+ tty_old_pgrp = 0, session = 914, tgid = 917, leader = 0,
+ p_opptr = 0xc2f94000, p_pptr = 0xc2f94000, p_cptr = 0x0, p_ysptr = 0x0,
+ p_osptr = 0x0, thread_group = {next = 0xc2da00a8, prev = 0xc2da00a8},
+ pidhash_next = 0x0, pidhash_pprev = 0xc01900b8, wait_chldexit = {
+ lock = <incomplete type>, task_list = {next = 0xc2da00b8,
+ prev = 0xc2da00b8}}, vfork_done = 0x0, rt_priority = 0,
+ it_real_value = 0, it_prof_value = 0, it_virt_value = 0, it_real_incr = 0,
+ it_prof_incr = 0, it_virt_incr = 0, real_timer = {list = {next = 0x0,
+ prev = 0x0}, expires = 18950, data = 3269066752,
+ function = 0xc000ce30 <it_real_fn>}, times = {tms_utime = 0,
+ tms_stime = 0, tms_cutime = 0, tms_cstime = 0}, start_time = 18989,
+ per_cpu_utime = {1}, per_cpu_stime = {310}, min_flt = 13, maj_flt = 104,
+ nswap = 0, cmin_flt = 0, cmaj_flt = 0, cnswap = 0, swappable = -1, uid = 0,
+ euid = 0, suid = 0, fsuid = 0, gid = 0, egid = 0, sgid = 0, fsgid = 0,
+ ngroups = 7, groups = {0, 1, 2, 3, 4, 6, 10, 0 <repeats 25 times>},
+ cap_effective = 4294967039, cap_inheritable = 0, cap_permitted = 4294967039,
+ keep_capabilities = 0, user = 0xc016b18c, rlim = {{rlim_cur = 4294967295,
+ rlim_max = 4294967295}, {rlim_cur = 4294967295, rlim_max = 4294967295}, {
+ rlim_cur = 4294967295, rlim_max = 4294967295}, {rlim_cur = 8388608,
+ rlim_max = 4294967295}, {rlim_cur = 0, rlim_max = 4294967295}, {
+ rlim_cur = 4294967295, rlim_max = 4294967295}, {rlim_cur = 512,
+ rlim_max = 512}, {rlim_cur = 1024, rlim_max = 1024}, {
+ rlim_cur = 4294967295, rlim_max = 4294967295}, {rlim_cur = 4294967295,
+ rlim_max = 4294967295}, {rlim_cur = 4294967295, rlim_max = 4294967295}},
+ used_math = 0, comm = "id\000h\000og\000\000\000\000\000\000\000\000",
+ link_count = 0, total_link_count = 1, tty = 0xc3ed1000, locks = 0,
+ semundo = 0x0, semsleeping = 0x0, thread = {esp0 = 3269074944,
+ eip = 3221249046, esp = 3269074792, fs = 0, gs = 0, io_pl = 3, debugreg = {
+ 0, 0, 0, 0, 0, 0, 0, 0}, cr2 = 0, trap_no = 0, error_code = 0, i387 = {
+ fsave = {cwd = 2098047, swd = 125632512, twd = 1073944696, fip = 2091,
+ fcs = -1073745032, foo = 2099, fos = 8064, st_space = {
+ 0 <repeats 20 times>}, status = 0}, fxsave = {cwd = 895, swd = 32,
+ twd = 0, fop = 1917, fip = 1073944696, fcs = 2091, foo = -1073745032,
+ fos = 2099, mxcsr = 8064, reserved = 0, st_space = {
+ 0 <repeats 24 times>, 1449431204, -1774489361, 16383, 0, 1,
+ -1891252224, 16404, 0}, xmm_space = {0 <repeats 32 times>},
+ padding = {0 <repeats 56 times>}}, soft = {cwd = 2098047,
+ swd = 125632512, twd = 1073944696, fip = 2091, fcs = -1073745032,
+ foo = 2099, fos = 8064, st_space = {0 <repeats 20 times>},
+ ftop = 0 '\0', changed = 0 '\0', lookahead = 0 '\0',
+ no_update = 0 '\0', rm = 0 '\0', alimit = 0 '\0', info = 0x0,
+ entry_eip = 0}}, vm86_info = 0x0, screen_bitmap = 0, v86flags = 0,
+ v86mask = 0, saved_esp0 = 0}, fs = 0x0, files = 0x0, namespace = 0x0,
+ sigmask_lock = <incomplete type>, sig = 0x0, blocked = {sig = {0, 0}},
+ pending = {head = 0x0, tail = 0xc2da04f8, signal = {sig = {0, 0}}},
+ sas_ss_sp = 0, sas_ss_size = 0, notifier = 0, notifier_data = 0x0,
+ notifier_mask = 0x0, parent_exec_id = 7, self_exec_id = 8,
+ alloc_lock = <incomplete type>, journal_info = 0x0}
--- /dev/null
+ROOT = ..
+
+CFILES = $(wildcard *.c)
+
+INSTALL_BINS := nsplitd
+
+include $(ROOT)/mk/rules.mk
+
+nsplitd: $(OBJS)
+ $(CC) $(CFLAGS) -o nsplitd $(OBJS)
+
--- /dev/null
+/*
+ * nsplitd.c
+ * ---------
+ *
+ * $Id: nsplitd.c,v 2.6 1998/09/17 14:28:37 sde1000 Exp $
+ *
+ * Copyright (c) 1995, University of Cambridge Computer Laboratory,
+ * Copyright (c) 1995, Richard Black, All Rights Reserved.
+ *
+ *
+ * A complete re-implementation of DME's nsplitd for use from inetd
+ *
+ */
+
+/* The basic stream comes in (via inetd) and we then conenct to
+ * somewhere else providing a loop-through service, except we offer
+ * two other ports for connection - one of which gets a second channel
+ * using the top bit to distinguish, and the other is a master control
+ * port (normally used for gdb) which gets complete exclusive access
+ * for its duration.
+ *
+ * Originally designed for multiplexing a xwcons/telnet with a gdb
+ * post-mortem debugging session.
+ *
+ * Here is a picture:
+ *
+ * port0 (from inetd)
+ * 8-bit connection /
+ * made by us <----> nsplitd <-----gdbport (default port0+2)
+ * to host:port/tcp |\
+ * | port1 (default port0+1)
+ * \
+ * control (default port0+3)
+ *
+ * If port1 is explicitly disabled (through a command-line option) then
+ * port0 becomes 8-bit clean.
+ */
+
+/*
+ * N.B.: We do NOT support 8 bit stdin/stdout usage on a
+ * /dev/... because to do that right involves much messing with ioctl
+ * and TIOC... etc. If you want to do that sort of thing then the
+ * right way to do it is to chain this onto wconsd (which does know
+ * about and understand all the ioctl and TIOC grief).
+ */
+
+#include <sys/types.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <string.h>
+
+#include <sys/time.h>
+#include <sys/signal.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <sys/ioctl.h>
+#include <syslog.h>
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef LOG_DAEMON
+#define LOG_DAEMON 0
+#endif
+
+#define DB(x) /* ((x), fflush(stderr)) */
+
+extern char *optarg;
+
+extern int optind, opterr, optopt;
+
+static char *prog_name;
+
+static void usage(void)
+{
+ fprintf(stderr, "This program (%s) should be run via inetd (tcp)\n\n",
+ prog_name);
+ fprintf(stderr, "usage: %s [-h<highport>][-g<gdbport>]"
+ "[-c<ctlport>][-8] host:service\n",
+ prog_name);
+ exit(1);
+}
+
+static void fault(char *format, ...)
+{
+ va_list ap;
+ char logbuf[1024];
+
+ va_start(ap, format);
+ fprintf(stderr, "%s: ", prog_name);
+ vfprintf(stderr, format, ap);
+ fflush(stderr);
+ va_end(ap);
+
+ /* XXX This is a bit dubious, but there is no vsyslog */
+ va_start(ap, format);
+ vsprintf(logbuf, format, ap);
+ syslog(LOG_ERR, logbuf);
+ va_end(ap);
+ exit(1);
+}
+
+static int getservice(char *name, unsigned short *port)
+{
+ struct servent *se;
+
+ if (!name) return -1;
+
+ if (isdigit(name[0]))
+ *port = atoi(name);
+ else
+ {
+ if (!(se = getservbyname(name, "tcp")))
+ return -1;
+ *port = ntohs(se->s_port);
+ }
+ return 0;
+}
+
+/*
+ * connect_host: connect to ("name", "port")
+ */
+static int connect_host (char *name, unsigned int port)
+{
+ int fd;
+ struct hostent *hostent;
+ struct sockaddr_in sin;
+ int on;
+
+ if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
+ fault("socket");
+
+ if (!(hostent = gethostbyname(name)))
+ fault("gethostbyname: %s: %s\n", name, strerror(errno));
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons (port);
+ memcpy(&sin.sin_addr.s_addr, hostent->h_addr, sizeof(struct in_addr));
+
+ if (connect(fd, (struct sockaddr *) &sin, sizeof (sin)) < 0)
+ fault("connect: %s:%u: %s\n", name, port, strerror(errno));
+
+ on = 1;
+ if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof (on)) < 0)
+ syslog(LOG_WARNING, "setsockopt (TCP_NODELAY): %m");
+
+ on = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0)
+ syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
+
+ return fd;
+}
+
+/*
+ * open a tcp socket and start listening for connections on it
+ */
+static int startlistening(unsigned short port)
+{
+ int fd, on;
+ struct sockaddr_in sin;
+
+ if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
+ fault("socket");
+
+ on = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0)
+ syslog(LOG_WARNING, "setsockopt (SO_REUSEADDR): %m");
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons (port);
+ sin.sin_addr.s_addr = INADDR_ANY;
+ if (bind(fd, &sin, sizeof(sin)) < 0)
+ fault("bind: %u: %s\n", port, strerror(errno));
+
+ if (listen(fd, 1) < 0)
+ fault("listen: %s\n", strerror(errno));
+
+ return fd;
+}
+
+static void noblock(int fd)
+{
+ int on=1;
+
+ if (ioctl(fd, FIONBIO, &on) < 0)
+ fault("ioctl: FIONBIO: %s\n", strerror(errno));
+}
+
+
+/* You might not believe this, but fd_sets don't have to be a 32-bit
+ * integer. In particular, in glibc2 it is an array of unsigned
+ * longs. Hence, this hacked up FD_SET_rjb() that works out if it
+ * would have been a nop. */
+#define FD_SET_rjb(fd, setp) \
+do { \
+ if ((fd) != 32) \
+ FD_SET((fd), (setp)); \
+} while(0)
+
+#define FD_ISSET_rjb(fd, setp) (((fd) != 32)? FD_ISSET((fd), (setp)) : 0)
+
+#define MAXSIZE 256
+
+/* -----------------------------------------------------------------
+ * The main bit of the algorithm. Note we use 32 to mean not connected
+ * because this gives us 1<<32 == 0. We could have done this one
+ * character at a time, but that would have been very inefficient and
+ * not the unix way. */
+static int debug;
+
+static void doit(int actl, int acto, int lish, int lisg, int lisc)
+{
+ int acth, actg, actc;
+ int gdbmode = FALSE;
+ char gibuf[MAXSIZE], oibuf[MAXSIZE];
+ char libuf[MAXSIZE], lobuf[MAXSIZE];
+ char hibuf[MAXSIZE], hobuf[MAXSIZE];
+ char ctlbuf[MAXSIZE];
+ fd_set rdfs, wrfs, exfs;
+ int gicc, oicc, licc, locc, hicc, hocc, ctlcc;
+ char *giptr, *oiptr, *liptr, *loptr, *hiptr, *hoptr;
+ int rc, fromlen;
+ struct sockaddr_in from;
+
+ gicc = oicc = licc = locc = hicc = hocc = ctlcc = 0;
+ acth = actg = actc = 32; /* XXX yummy */
+
+ noblock(actl);
+ noblock(acto);
+
+ for(;;)
+ {
+ FD_ZERO(&rdfs);
+ FD_ZERO(&wrfs);
+ FD_ZERO(&exfs);
+
+ /* always take input from the control port (if it's connected) */
+ FD_SET_rjb(actc, &rdfs);
+
+ if (gdbmode)
+ {
+ if (oicc)
+ FD_SET_rjb(actg, &wrfs);
+ else
+ FD_SET_rjb(acto, &rdfs);
+
+ if (gicc)
+ FD_SET_rjb(acto, &wrfs);
+ else
+ FD_SET_rjb(actg, &rdfs);
+ }
+ else
+ {
+ /* There is no such thing as oibuf because its been split into
+ * lobuf and hobuf
+ */
+ if (locc || hocc)
+ {
+ if (locc)
+ FD_SET_rjb(actl, &wrfs);
+ if (hocc)
+ FD_SET_rjb(acth, &wrfs);
+ }
+ else
+ FD_SET_rjb(acto, &rdfs);
+
+ if (licc)
+ FD_SET_rjb(acto, &wrfs);
+ else
+ FD_SET_rjb(actl, &rdfs);
+
+ if (hicc)
+ FD_SET_rjb(acto, &wrfs);
+ else
+ FD_SET_rjb(acth, &rdfs);
+ }
+
+ if (acth == 32 && lish>=0) FD_SET_rjb(lish, &rdfs);
+ if (actg == 32) FD_SET_rjb(lisg, &rdfs);
+ if (actc == 32) FD_SET_rjb(lisc, &rdfs);
+
+ /* now make exfs the union of the read and write fd sets, plus
+ * "actl" */
+ {
+ int i;
+ exfs = rdfs;
+ for(i=0; i<32; i++) /* XXX we only copy fd numbers up to 31 */
+ if (FD_ISSET(i, &wrfs))
+ FD_SET_rjb(i, &exfs);
+ FD_SET_rjb(actl, &exfs);
+ }
+
+ /* XXX AND: can't print something of type fd_set as %x - it
+ * might be an array */
+ DB(fprintf(stderr, "%s: before select: %08x %08x %08x\n",
+ prog_name, rdfs, wrfs, exfs));
+
+ if (select(32, &rdfs, &wrfs, &exfs, NULL) < 0)
+ fault("select: %s\n", strerror(errno));
+
+ DB(fprintf(stderr, "%s: after select: %08x %08x %08x\n",
+ prog_name, rdfs, wrfs, exfs));
+
+ /* XXX it appears that a non-blocking socket may not show up
+ * correctly in exfs but instead goes readable with no data in
+ * it. Thus we check for zero and goto the appropriate close
+ * method. */
+
+ /* Deal with exceptions */
+ if (FD_ISSET_rjb(actg, &exfs))
+ {
+ exfs_actg:
+ close(actg);
+ gdbmode = FALSE;
+ oicc = 0;
+ oiptr = oibuf;
+ actg = 32;
+ continue; /* because assumptions changed */
+ }
+ if (FD_ISSET_rjb(acth, &exfs))
+ {
+ exfs_acth:
+ close(acth);
+ hicc = hocc = 0;
+ hiptr = hibuf;
+ hoptr = hibuf;
+ acth = 32;
+ continue; /* because assumptions changed */
+ }
+ if (FD_ISSET_rjb(actl, &exfs) ||
+ FD_ISSET_rjb(acto, &exfs))
+ {
+ exfs_actl:
+ exfs_acto:
+ /* Thats all folks ... */
+ break;
+ }
+ if (FD_ISSET_rjb(actc, &exfs))
+ {
+ exfs_ctl:
+ close(actc);
+ actc = 32;
+ ctlcc = 0;
+ continue;
+ }
+
+ /* Deal with reading */
+ if (FD_ISSET_rjb(acto, &rdfs))
+ {
+ if ((oicc = read(acto, oiptr = oibuf, MAXSIZE)) < 0)
+ fault("read acto: %d: %s\n", oicc, strerror(errno));
+ if (!oicc) goto exfs_acto;
+
+ if (!gdbmode)
+ {
+ int t;
+
+ assert((locc == 0) && (hocc == 0));
+ loptr = lobuf;
+ hoptr = hobuf;
+
+ if (lish>=0) {
+ for(t=0; t<oicc; t++)
+ if (oibuf[t] & 0x80)
+ hobuf[hocc++] = oibuf[t] & 0x7f;
+ else
+ lobuf[locc++] = oibuf[t];
+ } else {
+ for (t=0; t<oicc; t++)
+ lobuf[locc++] = oibuf[t];
+ }
+ /* If no high connection scratch that */
+ if (acth == 32)
+ hocc=0;
+ }
+ }
+ if (FD_ISSET_rjb(actl, &rdfs))
+ {
+ if ((licc = read(actl, liptr = libuf, MAXSIZE)) < 0)
+ fault("read actl: %d: %s\n", licc, strerror(errno));
+ if (!licc) goto exfs_actl;
+ }
+ if (FD_ISSET_rjb(acth, &rdfs))
+ {
+ int t;
+
+ if ((hicc = read(acth, hiptr = hibuf, MAXSIZE)) < 0)
+ fault("read acth: %d: %s\n", hicc, strerror(errno));
+ if (!hicc) goto exfs_acth;
+ for(t=0; t<hicc; t++)
+ hibuf[t] |= 0x80;
+ }
+ if (FD_ISSET_rjb(actg, &rdfs))
+ {
+ if ((gicc = read(actg, giptr = gibuf, MAXSIZE)) < 0)
+ fault("read actg: %d: %s\n", gicc, strerror(errno));
+ if (debug) write(1, giptr, gicc); /* XXX */
+ if (!gicc) goto exfs_actg;
+ }
+ if (FD_ISSET_rjb(actc, &rdfs))
+ {
+ if ((ctlcc = read(actc, ctlbuf, MAXSIZE)) < 0)
+ fault("read actc: %d: %s\n", ctlcc, strerror(errno));
+ if (debug) write(1, ctlbuf, gicc);
+ if (!ctlcc) goto exfs_ctl;
+ if (ctlbuf[0] == 'r') /* reset command */
+ {
+ syslog(LOG_INFO, "reset command read, exiting");
+ if (debug) write(1, "reseting\n", sizeof("reseting\n"));
+ break;
+ }
+ }
+
+ /* Deal with writing */
+ if (FD_ISSET_rjb(actg, &wrfs))
+ {
+ /* We must be in gdb mode so send oi buffer data */
+ assert(gdbmode);
+ if (debug) write(2, oiptr, oicc); /* XXX */
+ if ((rc = write(actg, oiptr, oicc)) <= 0)
+ fault("write actg: %d: %s\n", rc, strerror(errno));
+ oiptr += rc;
+ oicc -= rc;
+ }
+ if (FD_ISSET_rjb(actl, &wrfs))
+ {
+ if ((rc = write(actl, loptr, locc)) <= 0)
+ fault("write actl: %d: %s\n", rc, strerror(errno));
+ loptr += rc;
+ locc -= rc;
+ }
+ if (FD_ISSET_rjb(acth, &wrfs))
+ {
+ if ((rc = write(acth, hoptr, hocc)) <= 0)
+ fault("write acth: %d: %s\n", rc, strerror(errno));
+ hoptr += rc;
+ hocc -= rc;
+ }
+ if (FD_ISSET_rjb(acto, &wrfs))
+ {
+ /* If in gdb mode send gdb input, otherwise send low data
+ preferentially */
+ if (gdbmode)
+ {
+ assert(gicc);
+ if ((rc = write(acto, giptr, gicc)) <= 0)
+ fault("write acto: %d: %s\n", rc, strerror(errno));
+ giptr += rc;
+ gicc -= rc;
+ }
+ else
+ {
+ if (licc)
+ {
+ if ((rc = write(acto, liptr, licc)) <= 0)
+ fault("write acto: %d: %s\n", rc, strerror(errno));
+ liptr += rc;
+ licc -= rc;
+ }
+ else
+ {
+ assert(hicc);
+ if ((rc = write(acto, hiptr, hicc)) <= 0)
+ fault("write acto: %d: %s\n", rc, strerror(errno));
+ hiptr += rc;
+ hicc -= rc;
+ }
+ }
+ }
+
+ /* Deals with new connections */
+ if ((acth == 32) && lish>=0 && (FD_ISSET_rjb(lish, &rdfs)))
+ {
+ fromlen = sizeof(from);
+ if ((acth = accept(lish, &from, &fromlen)) < 0)
+ {
+ syslog(LOG_WARNING, "accept: %m");
+ acth = 32;
+ }
+ else
+ {
+ noblock(acth);
+ hicc = hocc = 0;
+ syslog(LOG_INFO, "highbit client peer is %s:%u\n",
+ inet_ntoa(from.sin_addr), ntohs(from.sin_port));
+ }
+ }
+
+ if ((actg == 32) && (FD_ISSET_rjb(lisg, &rdfs)))
+ {
+ fromlen = sizeof(from);
+ if ((actg = accept(lisg, &from, &fromlen)) < 0)
+ {
+ syslog(LOG_WARNING, "accept: %m");
+ actg = 32;
+ }
+ else
+ {
+ noblock(actg);
+ gicc = 0;
+ gdbmode = TRUE;
+ syslog(LOG_INFO, "gdb client peer is %s:%u\n",
+ inet_ntoa(from.sin_addr), ntohs(from.sin_port));
+ }
+ }
+
+ if ((actc == 32) && (FD_ISSET_rjb(lisc, &rdfs)))
+ {
+ fromlen = sizeof(from);
+ if ((actc = accept(lisc, &from, &fromlen)) < 0)
+ {
+ syslog(LOG_WARNING, "accept (ctl): %m");
+ actc = 32;
+ }
+ else
+ {
+ noblock(actc);
+ syslog(LOG_INFO, "ctl client peer is %s:%u\n",
+ inet_ntoa(from.sin_addr), ntohs(from.sin_port));
+ }
+ }
+
+ /* Back to top of loop */
+ }
+
+ /* We are bailing because one of the primary connections has gone
+ * away. We close these all explicitly here because that way the
+ * timeout on reusing the port numbers is smnaller. */
+
+ close(acth);
+ close(actg);
+ /* XXX AND: why are we closing all these "character counts" ?? */
+ close(gicc);
+ close(oicc);
+ close(licc);
+ close(locc);
+ close(hicc);
+ close(hocc);
+}
+
+/*
+ * ------------------------------------------------------------
+ */
+int main(int argc, char **argv)
+{
+ /* In general, suffix "l" is low channel, "h" is high channel, "g"
+ * is gdb channel, "c" is control channel and "o" is output channel.
+ */
+ struct sockaddr_in from;
+ int infd = 0, outfd;
+ unsigned short portl, porth, portg, portc, porto;
+ int on = 1, c;
+ char *outname, *outservice;
+ int fromlen;
+ int lish, lisg, lisc;
+#if 0
+ FILE *newerr;
+#endif /* 0 */
+
+ prog_name = argv[0];
+
+ if (isatty(infd))
+ usage();
+
+ /* Here, then not just a simple idiot. */
+
+ signal(SIGPIPE, SIG_IGN);
+
+ openlog(prog_name, LOG_PID, LOG_DAEMON);
+
+ fromlen = sizeof(from);
+ if (getsockname(infd, &from, &fromlen) < 0)
+ fault("getsockname: %s", strerror(errno));
+ if ((fromlen != sizeof(from)) || (from.sin_family != AF_INET))
+ fault("not an inet socket (family=%d)\n", from.sin_family);
+
+ portl = ntohs(from.sin_port);
+ porth = portl+1;
+ portg = porth+1;
+ portc = portg+1;
+
+ fromlen = sizeof(from);
+ if (getpeername(infd, &from, &fromlen) < 0)
+ fault("getpeername: %s", strerror(errno));
+ if ((fromlen != sizeof(from)) || (from.sin_family != AF_INET))
+ fault("not an inet socket (family=%d)\n", from.sin_family);
+
+ syslog(LOG_INFO, "on port %u peer is %s:%u\n", portl,
+ inet_ntoa(from.sin_addr), ntohs(from.sin_port));
+
+ if (setsockopt(infd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0)
+ syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
+
+ /* from here on, we map stderr to output on the connection so we can
+ * report errors to the remote user.
+ */
+#if 0
+ if (!(newerr = fdopen(infd, "w")))
+ syslog(LOG_WARNING, "fdopen: %m");
+ else
+ *stderr = *newerr;
+#endif
+
+ while((c = getopt(argc, argv, "d8h:g:c:")) != EOF)
+ {
+ switch(c)
+ {
+ case 'd':
+ debug++;
+ break;
+
+ case 'h':
+ /* high bit port */
+ if (getservice(optarg, &porth) < 0)
+ fault("getservice failed (high port '%s')\n", optarg);
+ break;
+
+ case 'g':
+ /* gdb port */
+ if (getservice(optarg, &portg) < 0)
+ fault("getservice failed (gdb port '%s')\n", optarg);
+ break;
+
+ case 'c':
+ /* control port */
+ if (getservice(optarg, &portc) < 0)
+ fault("getservice failed (control port '%s')\n", optarg);
+ break;
+
+ case '8':
+ /* 8-bit clean; no high port */
+ porth=0;
+ break;
+
+ default:
+ fault("bad argument list!\n");
+ }
+ }
+
+ if (argc != optind + 1)
+ fault("unparsed arguments (%d!=%d)\n", argc, optind+1);
+
+ outname = argv[optind];
+ if (!(outservice = strchr(outname, ':')))
+ fault("output arg '%s' doesn't contain ':'\n", outname);
+ *outservice++ = 0;
+ if (getservice(outservice, &porto) < 0)
+ fault("getservice failed (output port '%s')\n", outservice);
+
+ /* Time to start the sockets */
+
+ if (porth) {
+ lish = startlistening(porth);
+ } else {
+ lish = -1;
+ }
+ lisg = startlistening(portg);
+ lisc = startlistening(portc);
+
+ outfd = connect_host(outname, porto);
+
+ doit(infd, outfd, lish, lisg, lisc);
+
+ syslog(LOG_INFO, "terminating normally\n");
+
+ fclose(stderr);
+
+ closelog();
+ exit(0);
+}
+
+/* End $Id: nsplitd.c,v 2.6 1998/09/17 14:28:37 sde1000 Exp $ */
--- /dev/null
+#include <xeno/lib.h>
+#include <xeno/sched.h>
+#include <asm-i386/ptrace.h>
+#include <xeno/keyhandler.h>
+#include <asm/pdb.h>
+#include <xeno/list.h>
+
+#undef DEBUG_TRACE
+#ifdef DEBUG_TRACE
+#define TRC(_x) _x
+#else
+#define TRC(_x)
+#endif
+
+#define BUFMAX 400
+
+#define PDB_DOMAIN_OFFSET 2 /* all domains are positive numbers */
+
+static const char hexchars[]="0123456789abcdef";
+
+int remote_debug;
+
+int pdb_foobar = 0x123456; /* testing */
+char *pdb_foobaz = "cambridge"; /* testing */
+
+#define PDB_BUFMAX 1024
+static char pdb_in_buffer[PDB_BUFMAX];
+static char pdb_out_buffer[PDB_BUFMAX];
+static char pdb_buffer[PDB_BUFMAX];
+static int pdb_in_buffer_ptr;
+static unsigned char pdb_in_checksum;
+static unsigned char pdb_xmit_checksum;
+
+int pdb_ctrl_thread = -1;
+int pdb_info_thread = -1;
+int pdb_stepping = 0;
+
+int hex (char);
+char *mem2hex (char *, char *, int);
+char *hex2mem (char *, char *, int);
+int hexToInt (char **ptr, int *intValue);
+
+void pdb_put_packet (unsigned char *buffer, int ack);
+void pdb_put_char (u_char c);
+u_char pdb_get_char ();
+
+static volatile int mem_err = 0;
+void set_mem_err (void) /* NOT USED YET... */
+{
+ mem_err = 1;
+}
+
+/* These are separate functions so that they are so short and sweet
+ that the compiler won't save any registers (if there is a fault
+ to mem_fault, they won't get restored, so there better not be any
+ saved). */
+int
+get_char (char *addr)
+{
+ return *addr;
+}
+
+void
+set_char (char *addr, int val)
+{
+ *addr = val;
+}
+
+void
+pdb_process_query (char *ptr)
+{
+ if (strcmp(ptr, "C") == 0)
+ {
+ /* empty string */
+ }
+ else if (strcmp(ptr, "fThreadInfo") == 0)
+ {
+ struct task_struct *p = &idle0_task;
+ u_long flags;
+ int count = 0, buf_idx = 0;
+
+ read_lock_irqsave (&tasklist_lock, flags);
+
+ pdb_out_buffer[buf_idx++] = 'm';
+ while ( (p = p->next_task) != &idle0_task )
+ {
+ int domain = p->domain + PDB_DOMAIN_OFFSET;
+
+ if (count > 0)
+ pdb_out_buffer[buf_idx++] = ',';
+ /*
+ if (domain < 0)
+ { pdb_out_buffer[buf_idx++] = '-'; domain = domain * -1; }
+ */
+ if (domain > 15)
+ {
+ pdb_out_buffer[buf_idx++] = hexchars[domain >> 4];
+ }
+ pdb_out_buffer[buf_idx++] = hexchars[domain % 16];
+ count++;
+ }
+ pdb_out_buffer[buf_idx++] = 'l';
+ pdb_out_buffer[buf_idx++] = 0;
+
+ read_unlock_irqrestore(&tasklist_lock, flags);
+ }
+ else if (strcmp(ptr, "sThreadInfo") == 0)
+ {
+ }
+ else if (strncmp(ptr, "ThreadExtraInfo,", 16) == 0)
+ {
+ int thread = 0;
+ char *message = "whatever!";
+
+ ptr += 16;
+ if (hexToInt (&ptr, &thread))
+ {
+ mem2hex ((char *)message, pdb_out_buffer, strlen(message) + 1);
+ }
+ }
+ else if (strcmp(ptr, "Offsets") == 0)
+ {
+ /* empty string */
+ }
+ else if (strncmp(ptr, "Symbol", 6) == 0)
+ {
+ strcpy (pdb_out_buffer, "OK");
+ }
+ else
+ {
+ printk("pdb_process_query: unknown query [%s]\n", ptr);
+ }
+}
+
+int
+pdb_process_command (char *ptr, struct pt_regs *regs)
+{
+ int sigval = 10;
+ int length;
+ unsigned long addr;
+ int ack = 1; /* wait for ack in pdb_put_packet */
+ int go = 0;
+
+ TRC(printk("pdb: [%s]\n", ptr));
+ {
+ pdb_out_buffer[0] = 0;
+
+ switch (*ptr++)
+ {
+ case '?':
+ pdb_out_buffer[0] = 'S';
+ pdb_out_buffer[1] = hexchars[sigval >> 4];
+ pdb_out_buffer[2] = hexchars[sigval % 16];
+ pdb_out_buffer[3] = 0;
+ break;
+ case 'S': /* step with signal */
+ case 's': /* step */
+ regs->eflags |= 0x100;
+ pdb_stepping = 1;
+ return 1;
+ /* not reached */
+ case 'C': /* continue with signal */
+ case 'c': /* continue */
+ regs->eflags &= ~0x100;
+ /* jump out before replying to gdb */
+ return 1;
+ /* not reached */
+ case 'd':
+ remote_debug = !(remote_debug); /* toggle debug flag */
+ break;
+ case 'D': /* detach */
+ return go;
+ /* not reached */
+ case 'g': /* return the value of the CPU registers */
+ {
+ int idx = 0;
+ mem2hex ((char *)®s->eax, &pdb_out_buffer[idx], sizeof(regs->eax));
+ idx += sizeof(regs->eax) * 2;
+ mem2hex ((char *)®s->ecx, &pdb_out_buffer[idx], sizeof(regs->ecx));
+ idx += sizeof(regs->ecx) * 2;
+ mem2hex ((char *)®s->edx, &pdb_out_buffer[idx], sizeof(regs->edx));
+ idx += sizeof(regs->edx) * 2;
+ mem2hex ((char *)®s->ebx, &pdb_out_buffer[idx], sizeof(regs->ebx));
+ idx += sizeof(regs->ebx) * 2;
+ mem2hex ((char *)®s->esp, &pdb_out_buffer[idx], sizeof(regs->esp));
+ idx += sizeof(regs->esp) * 2;
+ mem2hex ((char *)®s->ebp, &pdb_out_buffer[idx], sizeof(regs->ebp));
+ idx += sizeof(regs->ebp) * 2;
+ mem2hex ((char *)®s->esi, &pdb_out_buffer[idx], sizeof(regs->esi));
+ idx += sizeof(regs->esi) * 2;
+ mem2hex ((char *)®s->edi, &pdb_out_buffer[idx], sizeof(regs->edi));
+ idx += sizeof(regs->edi) * 2;
+ mem2hex ((char *)®s->eip, &pdb_out_buffer[idx], sizeof(regs->eip));
+ idx += sizeof(regs->eip) * 2;
+ mem2hex ((char *)®s->eflags, &pdb_out_buffer[idx], sizeof(regs->eflags));
+ idx += sizeof(regs->eflags) * 2;
+ mem2hex ((char *)®s->xcs, &pdb_out_buffer[idx], sizeof(regs->xcs));
+ idx += sizeof(regs->xcs) * 2;
+ mem2hex ((char *)®s->xss, &pdb_out_buffer[idx], sizeof(regs->xss));
+ idx += sizeof(regs->xss) * 2;
+ mem2hex ((char *)®s->xds, &pdb_out_buffer[idx], sizeof(regs->xds));
+ idx += sizeof(regs->xds) * 2;
+ mem2hex ((char *)®s->xes, &pdb_out_buffer[idx], sizeof(regs->xes));
+ idx += sizeof(regs->xes) * 2;
+ mem2hex ((char *)®s->xfs, &pdb_out_buffer[idx], sizeof(regs->xfs));
+ idx += sizeof(regs->xfs) * 2;
+ mem2hex ((char *)®s->xgs, &pdb_out_buffer[idx], sizeof(regs->xgs));
+
+ /*
+ TRC(printk (" reg: %s \n", pdb_out_buffer));
+ TRC(printk (" ebx: 0x%08lx\n", regs->ebx));
+ TRC(printk (" ecx: 0x%08lx\n", regs->ecx));
+ TRC(printk (" edx: 0x%08lx\n", regs->edx));
+ TRC(printk (" esi: 0x%08lx\n", regs->esi));
+ TRC(printk (" edi: 0x%08lx\n", regs->edi));
+ TRC(printk (" ebp: 0x%08lx\n", regs->ebp));
+ TRC(printk (" eax: 0x%08lx\n", regs->eax));
+ TRC(printk (" xds: 0x%08x\n", regs->xds));
+ TRC(printk (" xes: 0x%08x\n", regs->xes));
+ TRC(printk (" xfs: 0x%08x\n", regs->xfs));
+ TRC(printk (" xgs: 0x%08x\n", regs->xgs));
+ TRC(printk (" eip: 0x%08lx\n", regs->eip));
+ TRC(printk (" xcs: 0x%08x\n", regs->xcs));
+ TRC(printk (" efl: 0x%08lx\n", regs->eflags));
+ TRC(printk (" esp: 0x%08lx\n", regs->esp));
+ TRC(printk (" xss: 0x%08x\n", regs->xss));
+ */
+
+ break;
+ }
+ case 'G': /* set the value of the CPU registers - return OK */
+ break;
+
+ case 'H':
+ {
+ int thread;
+ char *next = &ptr[1];
+ if (hexToInt (&next, &thread))
+ {
+ if (thread > 0)
+ {
+ thread = thread - PDB_DOMAIN_OFFSET;
+ }
+ if (*ptr == 'c')
+ {
+ pdb_ctrl_thread = thread;
+ }
+ else if (*ptr == 'g')
+ {
+ pdb_info_thread = thread;
+ }
+ else
+ {
+ printk ("ack, unknown command %c (thread: %d)\n",
+ *ptr, thread);
+ }
+ }
+ strcpy (pdb_out_buffer, "OK");
+ break;
+ }
+ case 'k': /* kill request */
+ {
+ strcpy (pdb_out_buffer, "OK"); /* ack for fun */
+ printk ("don't kill bill...\n");
+ ack = 0;
+ break;
+ }
+
+ case 'q':
+ {
+ pdb_process_query(ptr);
+ break;
+ }
+
+ /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */
+ case 'm':
+ {
+ /* TRY TO READ %x,%x. IF SUCCEED, SET PTR = 0 */
+ if (hexToInt (&ptr, (int *)&addr))
+ if (*(ptr++) == ',')
+ if (hexToInt (&ptr, &length))
+ {
+ ptr = 0;
+ mem_err = 0;
+
+ if (pdb_info_thread >= 0)
+ {
+ pdb_get_values(pdb_info_thread, pdb_buffer, addr, length);
+ mem2hex (pdb_buffer, pdb_out_buffer, length);
+ }
+ else
+ mem2hex ((char *) addr, pdb_out_buffer, length);
+ if (mem_err)
+ {
+ strcpy (pdb_out_buffer, "E03");
+ }
+ }
+
+ if (ptr)
+ {
+ strcpy (pdb_out_buffer, "E01");
+ }
+ break;
+ }
+
+ /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
+ case 'M':
+ {
+ /* TRY TO READ '%x,%x:'. IF SUCCEED, SET PTR = 0 */
+ if (hexToInt (&ptr, (int *)&addr))
+ if (*(ptr++) == ',')
+ if (hexToInt (&ptr, &length))
+ if (*(ptr++) == ':')
+ {
+ mem_err = 0;
+
+ pdb_set_values(pdb_info_thread,
+ ptr, addr, length);
+
+ if (mem_err)
+ {
+ strcpy (pdb_out_buffer, "E03");
+ }
+ else
+ {
+ strcpy (pdb_out_buffer, "OK");
+ }
+
+ ptr = 0;
+ }
+ if (ptr)
+ {
+ strcpy (pdb_out_buffer, "E02");
+ }
+ break;
+ }
+ case 'T':
+ {
+ int thread;
+ if (hexToInt (&ptr, &thread))
+ {
+ thread -= PDB_DOMAIN_OFFSET;
+ struct task_struct *p = find_domain_by_id(thread);
+ if (p == NULL)
+ {
+ strcpy (pdb_out_buffer, "E00");
+ }
+ else
+ {
+ strcpy (pdb_out_buffer, "OK");
+ }
+ put_task_struct(p);
+ }
+ break;
+ }
+ } /* switch */
+
+ /* reply to the request */
+ pdb_put_packet (pdb_out_buffer, ack);
+ }
+
+ return go;
+}
+
+/*
+ * process an input character from the serial line.
+ *
+ * return "1" if the character is a gdb debug string
+ * (and hence shouldn't be further processed).
+ */
+
+int pdb_debug_state = 0; /* small parser state machine */
+
+int pdb_serial_input(u_char c, struct pt_regs *regs)
+{
+ int out = 1;
+ int loop, count;
+
+ switch (pdb_debug_state)
+ {
+ case 0: /* not currently processing debug string */
+ if ( c == '$' ) /* start token */
+ {
+ pdb_debug_state = 1;
+ pdb_in_buffer_ptr = 0;
+ pdb_in_checksum = 0;
+ pdb_xmit_checksum = 0;
+ }
+ else
+ {
+ out = 0;
+ }
+ break;
+ case 1: /* saw '$' */
+ if ( c == '#' ) /* checksum token */
+ {
+ pdb_debug_state = 2;
+ pdb_in_buffer[pdb_in_buffer_ptr] = 0;
+ }
+ else
+ {
+ pdb_in_checksum += c;
+ pdb_in_buffer[pdb_in_buffer_ptr++] = c;
+ }
+ break;
+ case 2: /* 1st checksum digit */
+ pdb_xmit_checksum = hex(c) << 4;
+ pdb_debug_state = 3;
+ break;
+ case 3: /* 2nd checksum digit */
+ pdb_xmit_checksum += hex(c);
+ if (pdb_in_checksum != pdb_xmit_checksum)
+ {
+ pdb_put_char('-'); /* checksum failure */
+ printk ("checksum failure [%s.%02x.%02x]\n", pdb_in_buffer,
+ pdb_in_checksum, pdb_xmit_checksum);
+ }
+ else
+ {
+ pdb_put_char('+'); /* checksum okay */
+ if ( pdb_in_buffer_ptr > 1 && pdb_in_buffer[2] == ':' )
+ {
+ pdb_put_char(pdb_in_buffer[0]);
+ pdb_put_char(pdb_in_buffer[1]);
+ /* remove sequence chars from buffer */
+ count = strlen(pdb_in_buffer);
+ for (loop = 3; loop < count; loop++)
+ pdb_in_buffer[loop - 3] = pdb_in_buffer[loop];
+ }
+
+ pdb_process_command (pdb_in_buffer, regs);
+ }
+ pdb_debug_state = 0;
+ break;
+ }
+
+ return out;
+}
+
+int hex(char ch)
+{
+ if ((ch >= 'a') && (ch <= 'f')) return (ch-'a'+10);
+ if ((ch >= '0') && (ch <= '9')) return (ch-'0');
+ if ((ch >= 'A') && (ch <= 'F')) return (ch-'A'+10);
+ return (-1);
+}
+
+/* convert the memory pointed to by mem into hex, placing result in buf */
+/* return a pointer to the last char put in buf (null) */
+char *
+mem2hex (mem, buf, count)
+ char *mem;
+ char *buf;
+ int count;
+{
+ int i;
+ unsigned char ch;
+
+ for (i = 0; i < count; i++)
+ {
+ ch = get_char (mem++);
+ *buf++ = hexchars[ch >> 4];
+ *buf++ = hexchars[ch % 16];
+ }
+ *buf = 0;
+ return (buf);
+}
+
+/* convert the hex array pointed to by buf into binary to be placed in mem */
+/* return a pointer to the character AFTER the last byte written */
+char *
+hex2mem (buf, mem, count)
+ char *buf;
+ char *mem;
+ int count;
+{
+ int i;
+ unsigned char ch;
+
+ for (i = 0; i < count; i++)
+ {
+ ch = hex (*buf++) << 4;
+ ch = ch + hex (*buf++);
+ set_char (mem++, ch);
+ }
+ return (mem);
+}
+
+int
+hexToInt (char **ptr, int *intValue)
+{
+ int numChars = 0;
+ int hexValue;
+ int negative = 0;
+
+ *intValue = 0;
+
+ if (**ptr == '-')
+ {
+ negative = 1;
+ numChars++;
+ (*ptr)++;
+ }
+ while (**ptr)
+ {
+ hexValue = hex (**ptr);
+ if (hexValue >= 0)
+ {
+ *intValue = (*intValue << 4) | hexValue;
+ numChars++;
+ }
+ else
+ break;
+
+ (*ptr)++;
+ }
+ if (negative)
+ {
+ *intValue *= -1;
+ }
+
+ return (numChars);
+}
+
+/***********************************************************************/
+/***********************************************************************/
+
+
+/*
+ * Add a breakpoint to the list of known breakpoints.
+ * For now there should only be two or three breakpoints so
+ * we use a simple linked list. In the future, maybe a red-black tree?
+ */
+struct pdb_breakpoint breakpoints;
+
+
+void pdb_bkpt_add (unsigned long address)
+{
+ struct pdb_breakpoint *bkpt;
+
+ bkpt = kmalloc(sizeof(struct pdb_breakpoint), GFP_KERNEL);
+ INIT_LIST_HEAD(&bkpt->list);
+
+ bkpt->address = address;
+
+ list_add(&bkpt->list, &breakpoints.list);
+
+ return;
+}
+
+/*
+ * Check to see of the breakpoint is in the list of known breakpoints
+ *
+ * return 1 if it has been set, 0 otherwise
+ */
+
+struct pdb_breakpoint* pdb_bkpt_search (unsigned long address)
+{
+ struct pdb_breakpoint *found = NULL;
+ struct list_head *list_entry;
+ struct pdb_breakpoint *bkpt;
+
+ list_for_each(list_entry, &breakpoints.list)
+ {
+ bkpt = list_entry(list_entry, struct pdb_breakpoint, list);
+
+ if (bkpt->address == address)
+ {
+ found = bkpt;
+ break;
+ }
+ }
+
+ return found;
+}
+
+/*
+ * Remove a breakpoint to the list of known breakpoints.
+ *
+ * Return 1 if the element was not found, otherwise 0.
+ */
+
+void pdb_bkpt_remove_ptr (struct pdb_breakpoint *bkpt)
+{
+ struct list_head *list_entry = &bkpt->list;
+ list_del(list_entry);
+ kfree(bkpt);
+}
+
+int pdb_bkpt_remove (unsigned long address)
+{
+ struct list_head *list_entry;
+ struct pdb_breakpoint *bkpt;
+ int found = 1;
+
+ list_for_each(list_entry, &breakpoints.list)
+ {
+ bkpt = list_entry(list_entry, struct pdb_breakpoint, list);
+
+ if (bkpt->address == address)
+ {
+ pdb_bkpt_remove_ptr (bkpt);
+ found = 0;
+ break;
+ }
+ }
+
+ return found;
+}
+
+/***********************************************************************/
+
+void breakpoint(void);
+
+int pdb_initialized = 0;
+int pdb_high_bit = 1;
+
+void pdb_put_char (u_char c)
+{
+ extern void debug_putchar(u_char);
+ u_char cc = pdb_high_bit ? c | 0x80 : c;
+ debug_putchar(cc);
+}
+
+u_char pdb_get_char ()
+{
+ extern u_char debug_getchar();
+ u_char cc = debug_getchar();
+ return cc & 0x7f;
+}
+
+/* send the packet in buffer. */
+void pdb_put_packet (unsigned char *buffer, int ack)
+{
+ unsigned char checksum;
+ int count;
+ char ch;
+
+ /* $<packet info>#<checksum> */
+ /* do */
+ {
+ pdb_put_char ('$');
+ checksum = 0;
+ count = 0;
+
+ while ((ch = buffer[count]))
+ {
+ pdb_put_char (ch);
+ checksum += ch;
+ count += 1;
+ }
+
+ pdb_put_char('#');
+ pdb_put_char(hexchars[checksum >> 4]);
+ pdb_put_char(hexchars[checksum % 16]);
+ }
+
+ if (ack)
+ {
+ if ((ch = pdb_get_char()) != '+')
+ {
+ printk(" pdb return error: %c 0x%x [%s]\n", ch, ch, buffer);
+ }
+ }
+}
+
+void pdb_get_packet(char *buffer)
+{
+ int count;
+ char ch;
+ unsigned char checksum = 0;
+ unsigned char xmitcsum = 0;
+
+ do
+ {
+ while ((ch = pdb_get_char()) != '$');
+
+ count = 0;
+ checksum = 0;
+
+ while (count < BUFMAX)
+ {
+ ch = pdb_get_char();
+ if (ch == '#') break;
+ checksum += ch;
+ buffer[count] = ch;
+ count++;
+ }
+ buffer[count] = 0;
+
+ if (ch == '#')
+ {
+ xmitcsum = hex(pdb_get_char()) << 4;
+ xmitcsum += hex(pdb_get_char());
+
+ if (xmitcsum == checksum)
+ {
+ pdb_put_char('+');
+ if (buffer[2] == ':')
+ {
+ printk ("gdb packet found with sequence ID\n");
+ }
+ }
+ else
+ {
+ pdb_put_char('-');
+ }
+ }
+ } while (checksum != xmitcsum);
+
+ return;
+}
+
+/*
+ * process a machine interrupt or exception
+ * return 1 if pdb is not interested in the exception; it should
+ * be propagated to the guest os.
+ */
+
+int pdb_handle_exception(int exceptionVector,
+ struct pt_regs *xen_regs)
+{
+ int signal = 0;
+
+ printk ("pdb_handle_exception [0x%x][0x%lx]\n",
+ exceptionVector, xen_regs->eip);
+
+ /* if pdb didn't set the breakpoint, and
+ pdb is not single stepping, and
+ the user didn't press the magic debug key on the console,
+ then pass the exception up to the guest os */
+ if (pdb_bkpt_search(xen_regs->eip - 1) == NULL &&
+ pdb_stepping == 0 &&
+ exceptionVector != 0x88)
+ {
+ TRC(printk("pdb: external breakpoint at 0x%lx\n", xen_regs->eip));
+ return 1;
+ }
+
+ if (pdb_stepping == 1)
+ {
+ xen_regs->eflags &= ~0x100;
+ pdb_stepping = 0;
+ }
+
+ if (exceptionVector == 0x03)
+ {
+ xen_regs->eip --;
+ }
+
+ /* generate a signal for gdb */
+ switch (exceptionVector)
+ {
+ case 136 : signal = 2; break; /* SIGINT */
+ case 1 : signal = 5; break; /* SIGTRAP */
+ case 3 : signal = 5; break; /* SIGTRAP */
+ default :
+ printk ("can't generate signal for unknown exception vector %d\n",
+ exceptionVector);
+ break;
+ }
+
+ pdb_out_buffer[0] = 'S';
+ pdb_out_buffer[1] = hexchars[signal >> 4];
+ pdb_out_buffer[2] = hexchars[signal % 16];
+ pdb_out_buffer[3] = 0;
+ pdb_put_packet(pdb_out_buffer, 1);
+
+ while (1)
+ {
+ pdb_out_buffer[0] = 0;
+ pdb_get_packet(pdb_in_buffer);
+ if (pdb_process_command(pdb_in_buffer, xen_regs))
+ {
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+void pdb_key_pressed(u_char key, void *dev_id, struct pt_regs *regs)
+{
+ pdb_handle_exception(136, regs);
+ return;
+}
+
+void initialize_pdb()
+{
+ extern char opt_pdb[];
+ int pdb_com_port;
+
+ if (strncmp(opt_pdb, "com", 3) == 0)
+ {
+ extern void debug_set_com_port(int port);
+
+ pdb_com_port = opt_pdb[3] - '0'; /* error checking ? */
+ debug_set_com_port(pdb_com_port);
+ pdb_high_bit = opt_pdb[4] == 'H' ? 1 : 0;
+ }
+ else
+ {
+ if (strcmp(opt_pdb, "none") != 0)
+ {
+ printk ("pdb: unknown option\n");
+ }
+ return;
+ }
+
+ printk ("Initializing pervasive debugger (PDB) [%s] port %d, high %d\n",
+ opt_pdb, pdb_com_port, pdb_high_bit);
+
+ breakpoints.address = 0;
+ INIT_LIST_HEAD(&breakpoints.list);
+
+ pdb_stepping = 0;
+
+ /* ack any spurrious gdb packets */
+ pdb_put_char ('+');
+
+ /* serial console */
+ add_key_handler('D', pdb_key_pressed, "enter pervasive debugger");
+
+ pdb_initialized = 1;
+}
+
+void breakpoint(void)
+{
+ if (pdb_initialized)
+ asm("int $3");
+}
#include <asm/apic.h>
#include <asm/desc.h>
#include <asm/domain_page.h>
+#include <asm/pdb.h>
struct cpuinfo_x86 boot_cpu_data = { 0 };
/* Lots of nice things, since we only target PPro+. */
do_initcalls();
initialize_serial(); /* setup serial 'driver' (for debugging) */
initialize_keyboard(); /* setup keyboard (also for debugging) */
+ initialize_pdb(); /* pervasive debugger */
if ( !setup_network_devices() )
panic("Must have a network device!\n");
#include <asm/pgalloc.h>
#include <asm/uaccess.h>
#include <asm/i387.h>
+#include <asm/pdb.h>
#define GTBF_TRAP 1
#define GTBF_TRAP_NOCODE 2
smp_processor_id(), trapnr, str, error_code);
}
+static inline void do_int3_exception(int trapnr,
+ struct pt_regs *regs,
+ long error_code)
+{
+ struct task_struct *p = current;
+ struct guest_trap_bounce *gtb = guest_trap_bounce+smp_processor_id();
+ trap_info_t *ti;
+
+ if ((regs->xcs & 3) != 3)
+ {
+ pdb_handle_exception(trapnr, regs);
+ return;
+ }
+
+ ti = current->thread.traps + trapnr;
+ gtb->flags = GTBF_TRAP_NOCODE;
+ gtb->error_code = error_code;
+ gtb->cs = ti->cs;
+ gtb->eip = ti->address;
+ if ( TI_GET_IF(ti) )
+ clear_bit(EVENTS_MASTER_ENABLE_BIT, &p->shared_info->events_mask);
+ return;
+}
+
#define DO_ERROR_NOCODE(trapnr, str, name) \
asmlinkage void do_##name(struct pt_regs * regs, long error_code) \
{ \
}
DO_ERROR_NOCODE( 0, "divide error", divide_error)
-DO_ERROR_NOCODE( 3, "int3", int3)
DO_ERROR_NOCODE( 4, "overflow", overflow)
DO_ERROR_NOCODE( 5, "bounds", bounds)
DO_ERROR_NOCODE( 6, "invalid operand", invalid_op)
DO_ERROR_NOCODE(18, "machine check", machine_check)
DO_ERROR_NOCODE(19, "simd error", simd_coprocessor_error)
+asmlinkage void do_int3(struct pt_regs * regs, long error_code)
+{
+ if (pdb_initialized) do_int3_exception(3, regs, error_code);
+ else do_trap(3, "int3", regs, error_code, 0);
+}
+
asmlinkage void do_double_fault(void)
{
extern spinlock_t console_lock;
}
}
-
asmlinkage void do_debug(struct pt_regs * regs, long error_code)
{
unsigned int condition;
struct task_struct *tsk = current;
struct guest_trap_bounce *gtb = guest_trap_bounce+smp_processor_id();
+ /*
+ printk("do_debug_exceptionn [%lx][%lx][%x]\n",
+ error_code, regs->eip, regs->xcs);
+ */
+
+ __asm__ __volatile__("movl %%db6,%0" : "=r" (condition));
+
+ if ((condition & (1 << 14)) != (1 << 14))
+ {
+ printk ("\nwarning: debug trap w/o BS bit [0x%x]\n\n", condition);
+ }
+ __asm__("movl %0,%%db6" : : "r" (0));
+
+ if (pdb_handle_exception(1, regs)) /* propagate to domain */
+ {
+ tsk->thread.debugreg[6] = condition;
+
+ gtb->flags = GTBF_TRAP_NOCODE;
+ gtb->cs = tsk->thread.traps[1].cs;
+ gtb->eip = tsk->thread.traps[1].address;
+ }
+
+ return;
+}
+
+
+asmlinkage void do_debug_orig(struct pt_regs * regs, long error_code)
+{
+ unsigned int condition;
+ struct task_struct *tsk = current;
+ struct guest_trap_bounce *gtb = guest_trap_bounce+smp_processor_id();
+
__asm__ __volatile__("movl %%db6,%0" : "=r" (condition));
/* Mask out spurious debug traps due to lazy DR7 setting */
--- /dev/null
+#include <xeno/config.h>
+#include <xeno/types.h>
+#include <xeno/lib.h>
+#include <hypervisor-ifs/dom0_ops.h>
+
+#include "debug-linux.h"
+
+/*
+ * linux specific pdb stuff
+ */
+
+/*
+ static inline struct task_struct *find_task_by_pid(int pid)
+ {
+ struct task_struct *p, **htable = &pidhash[pid_hashfn(pid)];
+
+ for(p = *htable; p && p->pid != pid; p = p->pidhash_next) ;
+ return p;
+ }
+*/
+
+/* read a byte from a process */
+u_char pdb_linux_get_value (int domain, int pid, unsigned long addr)
+{
+ u_char result = 0;
+ unsigned long task_struct_p, mm_p, pgd, task_struct_pid;
+ unsigned long l2tab, page;
+
+ /* find the task_struct of the given process */
+ pdb_get_values(domain, (u_char *) &task_struct_p,
+ pidhash_addr + pid_hashfn(pid) * 4,
+ sizeof(task_struct_p));
+
+ /* find the correct task struct */
+ while (task_struct_p != (unsigned long)NULL)
+ {
+ pdb_get_values(domain, (u_char *) &task_struct_pid,
+ task_struct_p + task_struct_pid_offset,
+ sizeof(task_struct_pid));
+ if (task_struct_pid == pid)
+ {
+ break;
+ }
+
+ pdb_get_values(domain, (u_char *) &task_struct_p,
+ task_struct_p + task_struct_pidhash_next_offset,
+ sizeof(task_struct_p));
+ }
+ if (task_struct_p == (unsigned long)NULL)
+ {
+ /* oops */
+ printk ("error: couldn't find process 0x%x in domain %d\n", pid, domain);
+ return 0;
+ }
+
+ /* get the mm_struct within the task_struct */
+ pdb_get_values(domain, (u_char *) &mm_p,
+ task_struct_p + task_struct_mm_offset,
+ sizeof(mm_p));
+ /* get the page global directory (cr3) within the mm_struct */
+ pdb_get_values(domain, (u_char *) &pgd,
+ mm_p + mm_struct_pgd_offset,
+ sizeof(pgd));
+
+ /* get the l2 table entry */
+ pdb_get_values(domain, (u_char *) &l2tab,
+ pgd + (addr >> PGDIR_SHIFT) * 4,
+ sizeof(l2tab));
+ l2tab = (unsigned long)__va(machine_to_phys(domain, l2tab) & PAGE_MASK);
+
+ /* get the page table entry */
+ pdb_get_values(domain, (u_char *) &page,
+ l2tab + ((addr & L1_PAGE_BITS) >> PAGE_SHIFT) * 4,
+ sizeof(page));
+ page = (unsigned long)__va(machine_to_phys(domain, page) & PAGE_MASK);
+
+ /* get the byte */
+ pdb_get_values(domain, (u_char *) &result, page + (addr & ~PAGE_MASK),
+ sizeof(result));
+
+ return result;
+}
--- /dev/null
+#include <asm/pdb.h>
+
+/* from linux/sched.h */
+#define PIDHASH_SZ (4096 >> 2)
+#define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
+
+/* from asm-xeno/pgtable-2level.h */
+#define PGDIR_SHIFT 22
+#define PTRS_PER_PGD 1024
+
+/* from asm-xeno/page.h */
+#define PAGE_SHIFT 12
+#define PAGE_SIZE (1UL << PAGE_SHIFT)
+#define PAGE_MASK (~(PAGE_SIZE-1))
+
+#define __PAGE_OFFSET (0xC0000000)
+#define PAGE_OFFSET ((unsigned long)__PAGE_OFFSET)
+#define __pa(x) ((unsigned long)(x)-PAGE_OFFSET)
+#define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET))
+
+/* from debug.h */
+#define ENTRIES_PER_L1_PAGETABLE 1024
+#define L1_PAGE_BITS ( (ENTRIES_PER_L1_PAGETABLE - 1) << PAGE_SHIFT )
+
+
+/* adapted from asm-xeno/page.h */
+static inline unsigned long machine_to_phys(int domain, unsigned long machine)
+{
+ unsigned long phys;
+ pdb_get_values(domain, (u_char *) &phys,
+ (unsigned long) machine_to_phys_mapping + (machine >> PAGE_SHIFT) * 4,
+ sizeof(phys));
+ phys = (phys << PAGE_SHIFT) | (machine & ~PAGE_MASK);
+ return phys;
+}
+
+
+#define pidhash_addr 0xc018f260UL
+
+#define task_struct_mm_offset 0x2c
+#define task_struct_pid_offset 0x7c
+#define task_struct_pidhash_next_offset 0xb0
+#define mm_struct_pgd_offset 0x0c
+
+extern u_char pdb_linux_get_value (int domain, int pid, unsigned long addr);
#include <hypervisor-ifs/dom0_ops.h>
#include <xeno/sched.h>
#include <xeno/event.h>
+#include <asm/page.h>
+#include <asm/domain_page.h> /* [un]map_domain_mem */
+#include <asm/pdb.h>
-#define DEBUG_TRACE
+#undef DEBUG_TRACE
#ifdef DEBUG_TRACE
#define TRC(_x) _x
#else
#define TRC(_x)
#endif
+/****************************************************************************/
+
+int pdb_change_values (int domain, u_char *buffer, unsigned long addr,
+ int length, int rw);
+
+/*
+ * Set memory in a domain's address space
+ * Set "length" bytes at "address" from "domain" to the values in "buffer".
+ * Return the number of bytes set, 0 if there was a problem.
+ *
+ * THIS WILL BECOME A MACRO
+ */
+
+int pdb_set_values (int domain, u_char *buffer, unsigned long addr, int length)
+{
+ int count;
+ void *bkpt;
+ count = pdb_change_values(domain, buffer, addr, length, 2);
+
+ /* this is a bit x86 specific at the moment... */
+ if (length == 1 && buffer[0] == 'c' && buffer[1] == 'c')
+ {
+ /* inserting a new breakpoint */
+ pdb_bkpt_add (addr);
+ TRC(printk("pdb breakpoint detected at 0x%lx\n", addr));
+ }
+ else if ((bkpt = pdb_bkpt_search(addr)))
+ {
+ /* removing a breakpoint */
+ TRC(printk("pdb breakpoint cleared at 0x%lx\n", addr));
+ pdb_bkpt_remove_ptr(bkpt);
+ }
+
+ return count;
+}
+
+/*
+ * Read memory from a domain's address space.
+ * Fetch "length" bytes at "address" from "domain" into "buffer".
+ * Return the number of bytes read, 0 if there was a problem.
+ *
+ * THIS WILL BECOME A MACRO
+ */
+
+int pdb_get_values (int domain, u_char *buffer, unsigned long addr, int length)
+{
+ return pdb_change_values(domain, buffer, addr, length, 1);
+}
+
+/*
+ * Change memory in a domain's address space.
+ * Read or write "length" bytes at "address" from "domain" into/from "buffer".
+ * Return the number of bytes read, 0 if there was a problem.
+ * RW: 1 = read, 2 = write
+ */
+
+int pdb_change_values (int domain, u_char *buffer, unsigned long addr,
+ int length, int rw)
+{
+ struct task_struct *p;
+ l2_pgentry_t* l2_table = NULL;
+ l1_pgentry_t* l1_table = NULL;
+ u_char *page;
+ int bytes = 0;
+
+ extern char *hex2mem (char *, char *, int);
+
+ p = find_domain_by_id(domain);
+
+ if ((addr >> PAGE_SHIFT) == ((addr + length - 1) >> PAGE_SHIFT))
+ {
+ l2_table = map_domain_mem(pagetable_val(p->mm.pagetable));
+ l2_table += l2_table_offset(addr);
+ if (!(l2_pgentry_val(*l2_table) & _PAGE_PRESENT))
+ {
+ printk ("L2:0x%p (0x%lx) \n", l2_table, l2_pgentry_val(*l2_table));
+ goto exit2;
+ }
+
+ if (l2_pgentry_val(*l2_table) & _PAGE_PSE)
+ {
+#define PSE_PAGE_SHIFT L2_PAGETABLE_SHIFT
+#define PSE_PAGE_SIZE (1UL << PSE_PAGE_SHIFT)
+#define PSE_PAGE_MASK (~(PSE_PAGE_SIZE-1))
+
+#define L1_PAGE_BITS ( (ENTRIES_PER_L1_PAGETABLE - 1) << L1_PAGETABLE_SHIFT )
+
+#define pse_pgentry_to_phys(_x) (l2_pgentry_val(_x) & PSE_PAGE_MASK)
+
+ page = map_domain_mem(pse_pgentry_to_phys(*l2_table) +/* 10 bits */
+ (addr & L1_PAGE_BITS)); /* 10 bits */
+ page += addr & (PAGE_SIZE - 1); /* 12 bits */
+ }
+ else
+ {
+ l1_table = map_domain_mem(l2_pgentry_to_phys(*l2_table));
+ l1_table += l1_table_offset(addr);
+ if (!(l1_pgentry_val(*l1_table) & _PAGE_PRESENT))
+ {
+ printk ("L2:0x%p (0x%lx) L1:0x%p (0x%lx)\n",
+ l2_table, l2_pgentry_val(*l2_table),
+ l1_table, l1_pgentry_val(*l1_table));
+ goto exit1;
+ }
+
+ page = map_domain_mem(l1_pgentry_to_phys(*l1_table));
+ page += addr & (PAGE_SIZE - 1);
+ }
+
+ switch (rw)
+ {
+ case 1: /* read */
+ memcpy (buffer, page, length);
+ bytes = length;
+ break;
+ case 2: /* write */
+ hex2mem (buffer, page, length);
+ bytes = length;
+ break;
+ default: /* unknown */
+ printk ("error: unknown RW flag: %d\n", rw);
+ return 0;
+ }
+
+ unmap_domain_mem((void *)page);
+ exit1:
+ if (l1_table != NULL)
+ unmap_domain_mem((void *)l1_table);
+ exit2:
+ unmap_domain_mem((void *)l2_table);
+ }
+ else
+ {
+ /* read spans pages. need to recurse */
+ printk ("pdb memory SPAN! addr:0x%lx l: %x\n", addr, length);
+ }
+
+ put_task_struct(p);
+ return bytes;
+}
+
+
+/*
+ * interactively call pervasive debugger from a privileged domain
+ */
void pdb_do_debug (dom0_op_t *op)
{
op->u.debug.status = 0;
- op->u.debug.out1 = op->u.debug.in2 + 10;
- op->u.debug.out2 = op->u.debug.in1 + 100;
- TRC(printk("PDB: op:%c, dom:%x, in1:%x, in2:%x\n",
+ TRC(printk("PDB: op:%c, dom:%x, in1:%x, in2:%x, in3:%x, in4:%x\n",
op->u.debug.opcode, op->u.debug.domain,
- op->u.debug.in1, op->u.debug.in2));
+ op->u.debug.in1, op->u.debug.in2,
+ op->u.debug.in3, op->u.debug.in4));
+ /* NOT NOW
if (op->u.debug.domain == 0)
{
op->u.debug.status = 1;
return;
}
+ */
switch (op->u.debug.opcode)
{
- case 'r' :
+ case 'c' :
{
struct task_struct * p = find_domain_by_id(op->u.debug.domain);
if ( p != NULL )
}
break;
}
+ case 'r' :
+ {
+ int loop;
+ u_char x;
+
+ for (loop = 0; loop < op->u.debug.in2; loop++) /* length */
+ {
+ extern u_char pdb_linux_get_value (int domain, int pid, unsigned long addr);
+
+ if (loop % 8 == 0)
+ {
+ printk ("\n%08x ", op->u.debug.in1 + loop);
+ }
+ x = pdb_linux_get_value(op->u.debug.domain, /* domain */
+ op->u.debug.in3, /* pid */
+ op->u.debug.in1 + loop); /* addr */
+ printk (" %02x", x);
+ }
+ printk ("\n");
+ break;
+ }
+
case 's' :
{
unsigned long cpu_mask;
int opt_ignorebiostables=0;
/* opt_watchdog: If true, run a watchdog NMI on each processor. */
int opt_watchdog=0;
+/* opt_pdb: Name of serial port for Xen pervasive debugger (and enable pdb) */
+unsigned char opt_pdb[10] = "none";
static struct {
unsigned char *name;
{ "noreboot", OPT_BOOL, &opt_noreboot },
{ "ignorebiostables", OPT_BOOL, &opt_ignorebiostables },
{ "watchdog", OPT_BOOL, &opt_watchdog },
+ { "pdb", OPT_STR, &opt_pdb },
{ NULL, 0, NULL }
};
#define NS16550_MCR_OUT2 0x08 /* OUT2: interrupt mask */
#define NS16550_MCR_LOOP 0x10 /* Loop */
-#define SERIAL_BASE 0x3f8 /* XXX SMH: horrible hardwired COM1 */
+#define LSR_DR 0x01 /* Data ready */
+#define LSR_OE 0x02 /* Overrun */
+#define LSR_PE 0x04 /* Parity error */
+#define LSR_FE 0x08 /* Framing error */
+#define LSR_BI 0x10 /* Break */
+#define LSR_THRE 0x20 /* Xmit holding register empty */
+#define LSR_TEMT 0x40 /* Xmitter empty */
+#define LSR_ERR 0x80 /* Error */
-static int serial_echo = 0; /* default is not to echo; change with 'e' */
+#define SERIAL_COM1 0x3f8
+#define SERIAL_COM2 0x2f8
+int serial_com_base = SERIAL_COM1;
+int debug_com_base = SERIAL_COM1;
+
+
+static int serial_echo = 0; /* default is not to echo; change with '~' */
void toggle_echo(u_char key, void *dev_id, struct pt_regs *regs)
{
serial_echo = !serial_echo;
}
+void debug_set_com_port(int port)
+{
+ debug_com_base = port == 1 ? SERIAL_COM1 : SERIAL_COM2;
+}
+
+int debug_testchar() /* character available? */
+{
+ return (inb(debug_com_base + NS16550_LSR) & LSR_DR);
+}
+
+u_char debug_getchar()
+{
+ while (! (inb(debug_com_base + NS16550_LSR) & LSR_DR));/* wait for char */
+ return inb(debug_com_base + NS16550_RBR);
+}
+
+void debug_putch(u_char c)
+{
+ while (! (inb(debug_com_base + NS16550_LSR) & LSR_THRE));
+ /* wait for idle */
+ outb(c, debug_com_base + NS16550_RBR);
+}
+
+void debug_putchar(u_char c)
+{
+ debug_putch(c);
+ if (c == '\n') debug_putch('\r');
+}
+
+
+
+int serial_testchar() /* character available? */
+{
+ return (inb(serial_com_base + NS16550_LSR) & LSR_DR);
+}
+
+u_char serial_getchar()
+{
+ while (! (inb(serial_com_base + NS16550_LSR) & LSR_DR));/* wait for char */
+ return inb(serial_com_base + NS16550_RBR);
+}
+
+void serial_putch(u_char c)
+{
+ while (! (inb(serial_com_base + NS16550_LSR) & LSR_THRE));
+ /* wait for idle */
+ outb(c, serial_com_base + NS16550_RBR);
+}
+
+void serial_putchar(u_char c)
+{
+ serial_putch(c);
+ if (c == '\n') serial_putch('\r');
+}
+
+static spinlock_t serial_lock;
+
static void serial_rx_int(int irq, void *dev_id, struct pt_regs *regs)
{
u_char c;
key_handler *handler;
+ unsigned long flags;
- while ( (inb(SERIAL_BASE + NS16550_LSR) & 1) == 1 )
- {
- c = inb(SERIAL_BASE + NS16550_RBR);
-
- if( (handler = get_key_handler(c)) != NULL )
- (*handler)(c, dev_id, regs);
+ spin_lock_irqsave(&serial_lock, flags);
- if ( serial_echo )
- printk("%c", c);
- }
+ while (serial_testchar())
+ {
+ c = serial_getchar();
+
+ if (c & 0x80)
+ {
+ extern int pdb_serial_input(u_char, struct pt_regs *);
+ pdb_serial_input(c & 0x7f, regs);
+ }
+ else
+ {
+ if ( (handler = get_key_handler(c)) != NULL )
+ (*handler)(c, dev_id, regs);
+
+ if ( serial_echo )
+ serial_putch(c);
+ }
+ }
+
+ spin_unlock_irqrestore(&serial_lock, flags);
}
void initialize_serial()
if ( !SERIAL_ENABLED )
return;
+
+ spin_lock_init(&serial_lock);
/* setup key handler */
add_key_handler('~', toggle_echo, "toggle serial echo");
/* Clear FIFOs, enable, trigger at 1 byte */
outb(NS16550_FCR_TRG1 | NS16550_FCR_ENABLE |
NS16550_FCR_CLRX | NS16550_FCR_CLTX,
- SERIAL_BASE+NS16550_FCR);
+ serial_com_base + NS16550_FCR);
/* Enable receive interrupts. Also remember to keep DTR/RTS asserted. */
outb(NS16550_MCR_OUT2|NS16550_MCR_DTR|NS16550_MCR_RTS,
- SERIAL_BASE + NS16550_MCR);
+ serial_com_base + NS16550_MCR);
outb(NS16550_IER_ERDAI,
- SERIAL_BASE + NS16550_IER );
+ serial_com_base + NS16550_IER );
if( (rc = request_irq(4, serial_rx_int, SA_NOPROFILE, "serial", 0)) )
printk("initialize_serial: failed to get IRQ4, rc=%d\n", rc);
--- /dev/null
+
+/*
+ * pervasive debugger
+ *
+ * alex ho
+ * 2004
+ * university of cambridge computer laboratory
+ */
+
+
+#ifndef __PDB_H__
+#define __PDB_H__
+
+#include <asm/ptrace.h>
+#include <xeno/list.h>
+
+extern int pdb_initialized;
+extern int pdb_com_port;
+extern int pdb_high_bit;
+
+extern void initialize_pdb(void);
+extern int pdb_set_values (int domain, u_char *buffer,
+ unsigned long addr, int length);
+extern int pdb_get_values (int domain, u_char *buffer,
+ unsigned long addr, int length);
+
+extern int pdb_handle_exception(int exceptionVector,
+ struct pt_regs *xen_regs);
+
+
+struct pdb_breakpoint
+{
+ struct list_head list;
+ unsigned long address;
+};
+extern void pdb_bkpt_add (unsigned long address);
+extern struct pdb_breakpoint* pdb_bkpt_search (unsigned long address);
+extern void pdb_bkpt_remove_ptr (struct pdb_breakpoint *bkpt);
+extern int pdb_bkpt_remove (unsigned long address);
+
+#endif /* __PDB_H__ */
* This makes sure that old versions of dom0 tools will stop working in a
* well-defined way (rather than crashing the machine, for instance).
*/
-#define DOM0_INTERFACE_VERSION 0xAAAA0003
+#define DOM0_INTERFACE_VERSION 0xAAAA0004
/*
{
/* IN variables. */
char opcode;
- int domain, in1, in2;
+ unsigned int domain;
+ int in1, in2, in3, in4;
/* OUT variables. */
- int status, out1, out2;
+ unsigned int status;
+ int out1, out2;
} dom0_debug_t;
/*
* or expiring timer
* TASK_UNINTERRUPTIBLE: Domain is blocked but may not be woken up by an
* arbitrary event or timer.
- * TASK_STOPPED: Domain is sopped.
+ * TASK_STOPPED: Domain is stopped.
* TASK_DYING: Domain is about to cross over to the land of the dead.
*/